package com.zp.drug.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.zp.business.api.prescription.vo.PrescriptionApplyVo;
import com.zp.business.api.prescription.vo.PrescriptionItemVo;
import com.zp.common.core.constant.BaseConstants;
import com.zp.common.core.constant.CacheNames;
import com.zp.common.core.constant.DrugConstants;
import com.zp.common.core.constant.UserConstants;
import com.zp.common.core.utils.DateUtils;
import com.zp.common.core.utils.SnowIdUtils;
import com.zp.common.core.utils.StringUtils;
import com.zp.common.myBatis.core.page.PageQuery;
import com.zp.common.redis.utils.CacheUtils;
import com.zp.common.satoken.utils.LoginHelper;
import com.zp.drug.entity.DrugStock;
import com.zp.drug.entity.DrugStockAllLog;
import com.zp.drug.entity.bo.DrugImportMasterBo;
import com.zp.drug.entity.bo.DrugStockBo;
import com.zp.drug.entity.bo.GrantStockVsPrescriptionBo;
import com.zp.drug.entity.vo.AutoPrescriptionVo;
import com.zp.drug.entity.vo.DrugStockAllLogParamVo;
import com.zp.drug.entity.vo.DrugStockVo;
import com.zp.drug.entity.vo.GrantStockVsPrescriptionVo;
import com.zp.drug.mapper.DrugStockMapper;
import com.zp.drug.service.IDrugStockAllLogService;
import com.zp.drug.service.IDrugStockService;
import com.zp.drug.service.IGrantStockVsPrescriptionService;
import com.zp.system.api.entity.SysDictData;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 药品库存Service业务层处理
 *
 * @author zhang peng
 * @ date 2023-06-08
 */
@RequiredArgsConstructor
@Service
public class DrugStockServiceImpl implements IDrugStockService {

    private final DrugStockMapper baseMapper;

    private final IDrugStockAllLogService stockAllLogService;

    // 规格单位
    static List<SysDictData> SPEC_UNIT_MAP = CacheUtils.get(CacheNames.SYS_DICT, CacheNames.SPEC_UNIT);
    private final IGrantStockVsPrescriptionService grantStockVsPrescriptionService;


    /**
     * 查询药品库存
     */
    @Override
    public DrugStockVo queryById(String id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 查询药品库存列表(分页)
     */
    @Override
    public Map<String, Object> queryPageList(DrugStockBo bo, PageQuery pageQuery) {
        Map<String, Object> params = bo.getParams();
        Map<String, Object> map = new HashMap<>();
        if (params.get("beginTime") != null && params.get("endTime") != null) {
            bo.setBeginTime(DateUtils.dateTime(params.get("beginTime")));
            bo.setEndTime(DateUtils.dateTime(params.get("endTime")));
        }
        Page<DrugStockVo> result = baseMapper.getStockQuantity(pageQuery.build(), bo);
        result.getRecords().forEach(item -> {
            String daysOfFailure = DateUtils.getDatePoor(item.getExpireDate(), new Date());
            if (!DateUtils.eqDate(item.getExpireDate(), new Date())) {
                item.setDaysOfFailure("已过期");
            } else {
                item.setDaysOfFailure(daysOfFailure.substring(0, daysOfFailure.indexOf("天") + 1));
            }
        });
        map.put("tableDataInfo", result);
        // 获取库存总进价、总售价、总进销差
        DrugStockVo drugStockVo = baseMapper.getStockQuantitySum(bo);
        map.put("allPageTradePrice", 0.00);
        map.put("allPageRetailPrice", 0.00);
        map.put("allBalance", 0.00);
        if (ObjectUtil.isNotNull(drugStockVo)) {
            map.put("allPageTradePrice", drugStockVo.getAllPageTradePrice());
            map.put("allPageRetailPrice", drugStockVo.getAllPageRetailPrice());
            map.put("allBalance", drugStockVo.getAllBalance());
        }
        return map;
    }

    /**
     * 查询药品库存列表(不分页)
     */
    @Override
    public List<DrugStockVo> queryList(DrugStockBo bo) {
        LambdaQueryWrapper<DrugStock> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<DrugStock> buildQueryWrapper(DrugStockBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<DrugStock> lqw = Wrappers.lambdaQuery();
        lqw.eq(StringUtils.isNotBlank(bo.getDrugCode()), DrugStock::getDrugCode, bo.getDrugCode());
        lqw.eq(StringUtils.isNotBlank(bo.getDrugInfoId()), DrugStock::getDrugInfoId, bo.getDrugInfoId());
        lqw.eq(StringUtils.isNotBlank(bo.getDrugPriceId()), DrugStock::getDrugPriceId, bo.getDrugPriceId());
        lqw.eq(StringUtils.isNotBlank(bo.getPackSize()), DrugStock::getPackSize, bo.getPackSize());
        lqw.eq(StringUtils.isNotBlank(bo.getStorageId()), DrugStock::getStorageId, bo.getStorageId());
        lqw.like(StringUtils.isNotBlank(bo.getStorageName()), DrugStock::getStorageName, bo.getStorageName());
        lqw.eq(bo.getPackageNum() != null, DrugStock::getPackageNum, bo.getPackageNum());
        lqw.eq(StringUtils.isNotBlank(bo.getPackageSpec()), DrugStock::getPackageSpec, bo.getPackageSpec());
        lqw.eq(StringUtils.isNotBlank(bo.getPackageUnits()), DrugStock::getPackageUnits, bo.getPackageUnits());
        lqw.eq(StringUtils.isNotBlank(bo.getMinSpec()), DrugStock::getMinSpec, bo.getMinSpec());
        lqw.eq(StringUtils.isNotBlank(bo.getMinUnits()), DrugStock::getMinUnits, bo.getMinUnits());
        lqw.eq(bo.getTradePrice() != null, DrugStock::getTradePrice, bo.getTradePrice());
        lqw.eq(bo.getTradePriceMin() != null, DrugStock::getTradePriceMin, bo.getTradePriceMin());
        lqw.eq(bo.getRetailPrice() != null, DrugStock::getRetailPrice, bo.getRetailPrice());
        lqw.eq(bo.getRetailPriceMin() != null, DrugStock::getRetailPriceMin, bo.getRetailPriceMin());
        lqw.eq(bo.getNetworkPrice() != null, DrugStock::getNetworkPrice, bo.getNetworkPrice());
        lqw.eq(bo.getNetworkPriceMin() != null, DrugStock::getNetworkPriceMin, bo.getNetworkPriceMin());
        lqw.eq(bo.getDiscount() != null, DrugStock::getDiscount, bo.getDiscount());
        lqw.eq(StringUtils.isNotBlank(bo.getBatchNo()), DrugStock::getBatchNo, bo.getBatchNo());
        lqw.eq(bo.getProduceDate() != null, DrugStock::getProduceDate, bo.getProduceDate());
        lqw.eq(bo.getExpireDate() != null, DrugStock::getExpireDate, bo.getExpireDate());
        lqw.eq(bo.getQuantity() != null, DrugStock::getQuantity, bo.getQuantity());
        lqw.eq(StringUtils.isNotBlank(bo.getOrgId()), DrugStock::getOrgId, bo.getOrgId());
        lqw.eq(StringUtils.isNotBlank(bo.getDocumentNo()), DrugStock::getDocumentNo, bo.getDocumentNo());
        lqw.eq(StringUtils.isNotBlank(bo.getImportMasterId()), DrugStock::getImportMasterId, bo.getImportMasterId());
        lqw.eq(StringUtils.isNotBlank(bo.getLocation()), DrugStock::getLocation, bo.getLocation());
        lqw.eq(StringUtils.isNotBlank(bo.getSupplierId()), DrugStock::getSupplierId, bo.getSupplierId());
        lqw.eq(StringUtils.isNotBlank(bo.getInventoryIsShow()), DrugStock::getInventoryIsShow, bo.getInventoryIsShow());
        lqw.eq(StringUtils.isNotBlank(bo.getRemarks()), DrugStock::getRemarks, bo.getRemarks());
        lqw.eq(StringUtils.isNotBlank(bo.getIsSupply()), DrugStock::getIsSupply, bo.getIsSupply());
        lqw.eq(StringUtils.isNotBlank(bo.getStopOperator()), DrugStock::getStopOperator, bo.getStopOperator());
        lqw.eq(bo.getStopDate() != null, DrugStock::getStopDate, bo.getStopDate());
        lqw.eq(bo.getProportion() != null, DrugStock::getProportion, bo.getProportion());
        lqw.between(params.get("beginTime") != null && params.get("endTime") != null,
                DrugStock::getCreateTime, DateUtils.dateTime(params.get("beginTime")), DateUtils.dateTime(params.get("endTime")));
        lqw.orderByDesc(DrugStock::getCreateTime);
        return lqw;
    }

    /**
     * 新增药品库存
     */
    @Override
    public Boolean insertByBo(DrugStockBo bo) {
        DrugStock add = BeanUtil.toBean(bo, DrugStock.class);
        validEntityBeforeSave(add);
        add.initEntity();
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改药品库存
     */
    @Override
    public Boolean updateByBo(DrugStockBo bo) {
        DrugStock update = BeanUtil.toBean(bo, DrugStock.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(DrugStock entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 批量删除药品库存
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<String> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<DrugStock> batchDrugStockInfo(DrugImportMasterBo bo) {
        List<DrugStock> drugStocks = new ArrayList<>();
        bo.getStockBoList().forEach(item -> {
            DrugStock drugStock = BeanUtil.toBean(item, DrugStock.class);
            //  通过批号、价格、机构id、药品infoId 查询库存表中是否存在符合的药品
            DrugStockVo drugStockVo = selectDataIsExist(item);
            if (ObjectUtil.isNotNull(drugStockVo)) {
                //修改相同数据增加库存
                baseMapper.update(null, Wrappers.<DrugStock>lambdaUpdate()
                        .set(DrugStock::getQuantity, item.getQuantity() + drugStockVo.getQuantity())
                        .eq(DrugStock::getId, drugStockVo.getId())
                );
            } else {
                // 如果没有能够匹配上的库存数据，直接新增
                drugStock.initEntity();
                drugStock.setCreateBy("");
                drugStock.setCreateBy(null);
                drugStock.setUpdateBy("");
                drugStock.setUpdateTime(null);
                // 查询相同价格的库存
                String samePriceStock = getSamePriceStock(item);
                drugStock.setId(SnowIdUtils.uniqueString());
                drugStock.setIdenticalInfoId(samePriceStock);
                item.setId(drugStock.getId());
                baseMapper.insert(drugStock);
                drugStocks.add(drugStock);
            }
        });
        DrugStockAllLogParamVo drugStockAllLogParamVo = new DrugStockAllLogParamVo();
        drugStockAllLogParamVo.setRecordType(bo.getRecordType());
        drugStockAllLogParamVo.setDrugImportMasterBo(bo);
        // 保存库存流转明细
        stockAllLogService.caseRecordTypeSave(drugStockAllLogParamVo);
        return drugStocks;
    }

    public DrugStockVo selectDataIsExist(DrugStockBo bo) {
        List<DrugStockVo> drugStockVos = baseMapper.selectVoList(Wrappers.<DrugStock>lambdaQuery()
                .eq(DrugStock::getOrgId, bo.getOrgId())
                .eq(DrugStock::getTradePrice, bo.getTradePrice())
                .eq(DrugStock::getBatchNo, bo.getBatchNo())
                .eq(DrugStock::getDrugInfoId, bo.getDrugInfoId())
                .eq(DrugStock::getStorageId, bo.getStorageId())

        );
        if (CollectionUtil.isNotEmpty(drugStockVos)) {
            return drugStockVos.get(0);
        } else {
            return null;
        }
    }

    /**
     * 查询相同价格的库存
     *
     * @param bo
     */
    public String getSamePriceStock(DrugStockBo bo) {
        List<DrugStockVo> drugStockVos = baseMapper.selectVoList(Wrappers.<DrugStock>lambdaQuery()
                .eq(DrugStock::getOrgId, bo.getOrgId())
                .eq(DrugStock::getTradePriceMin, bo.getTradePriceMin())
                .eq(DrugStock::getRetailPriceMin, bo.getRetailPriceMin())
                .eq(DrugStock::getDrugInfoId, bo.getDrugInfoId())
                .eq(DrugStock::getStorageId, bo.getStorageId())

        );
        if (CollectionUtil.isNotEmpty(drugStockVos)) {
            return drugStockVos.get(0).getIdenticalInfoId();
        } else {
            // 生成新的相同项目id
            return SnowIdUtils.uniqueString();
        }
    }

    /**
     * 处方自动补全
     *
     * @param vo
     * @return`
     */
    @Override
    public List<AutoPrescriptionVo> autoPrescriptionList(AutoPrescriptionVo vo) {
        List<AutoPrescriptionVo> autoPrescriptionVoList = baseMapper.autoPrescriptionList(vo);
        autoExportListInit(autoPrescriptionVoList);
        return autoPrescriptionVoList;
    }


    @Override
    public List<AutoPrescriptionVo> autoExportList(AutoPrescriptionVo vo) {
        List<AutoPrescriptionVo> autoExportList = baseMapper.autoExportList(vo);
        autoExportListInit(autoExportList);
        return autoExportList;
    }

    public void autoExportListInit(List<AutoPrescriptionVo> autoExportList) {
        autoExportList.forEach(autoPrescriptionVo -> {
            // set 规格单位名称
            autoPrescriptionVo.setUnitsName(CollectionUtil.isNotEmpty(SPEC_UNIT_MAP) && StringUtils.isNotBlank(autoPrescriptionVo.getCurrentUnits()) ?
                    SPEC_UNIT_MAP.stream()
                            .filter(i -> StringUtils.equals(autoPrescriptionVo.getCurrentUnits(), i.getDictValue()))
                            .findAny().get().getDictLabel() : "");
            autoPrescriptionVo.setPackageSizeInfo(StringUtils.equals("2", autoPrescriptionVo.getPackSize()) ? "大包装" : "小包装");
            autoPrescriptionVo.setDoseUnitsAndName(autoPrescriptionVo.getDose() + " " + autoPrescriptionVo.getDoseUnitsName());
            autoPrescriptionVo.setQuantityAndUnits(autoPrescriptionVo.getQuantity() + " " + autoPrescriptionVo.getUnitsName());
        });
    }


    /**
     * 确认发放
     *
     * @param prescriptionApplyVos
     */
    @Override
    @GlobalTransactional
    public Boolean grantInfoOk(List<PrescriptionApplyVo> prescriptionApplyVos) {
        prescriptionApplyVos.forEach(prescriptionApplyVo -> {
            List<PrescriptionItemVo> itemVos = prescriptionApplyVo.getPrescriptionItemVos();
            itemVos.forEach(itemVo -> {
                // 通过药品规格、价钱相同，但批次不同的药品保存相同id 、发药药房、机构id获取符合条件的所有库存数据
                List<DrugStockVo> drugStockVos = baseMapper.selectVoList(Wrappers.<DrugStock>lambdaQuery()
                        .eq(DrugStock::getStorageId, prescriptionApplyVo.getImplementDept())
                        .eq(DrugStock::getOrgId, prescriptionApplyVo.getOrgId())
                        .eq(DrugStock::getIdenticalInfoId, itemVo.getIdenticalInfoId())
                        .eq(DrugStock::getIsSupply, UserConstants.YES)
                        .eq(DrugStock::getDelFlag, "0")
                        .gt(DrugStock::getQuantity, 0)
                        .orderByAsc(DrugStock::getExpireDate)
                );
                calculateInventoryDeductions(prescriptionApplyVo, itemVo, drugStockVos);
            });
        });
        return Boolean.TRUE;
    }

    /**
     * 确认退药
     *
     * @param prescriptionApplyVo
     * @return
     */
    @Override
    @Transactional
    public Boolean issuedGrantInfoOk(PrescriptionApplyVo prescriptionApplyVo) {
        GrantStockVsPrescriptionBo selectGrant = new GrantStockVsPrescriptionBo();
        selectGrant.setPrescriptionApplyId(prescriptionApplyVo.getApplyId());
        List<GrantStockVsPrescriptionVo> grantStockVsPrescriptionVos = grantStockVsPrescriptionService.queryList(selectGrant);
        if (CollectionUtil.isNotEmpty(grantStockVsPrescriptionVos)) {
            grantStockVsPrescriptionVos.forEach(vo -> {
                DrugStockVo drugStockVo = baseMapper.selectVoOne(Wrappers.<DrugStock>lambdaQuery()
                        .eq(DrugStock::getId, vo.getStockId())
                );
                baseMapper.update(null, Wrappers.<DrugStock>lambdaUpdate()
                        .set(DrugStock::getQuantity, drugStockVo.getQuantity() + vo.getNum())
                        .eq(DrugStock::getId, vo.getStockId())
                );
                List<PrescriptionItemVo> prescriptionItemVos = prescriptionApplyVo.getPrescriptionItemVos().stream()
                        .filter(itemVo -> StringUtils.equals(vo.getPrescriptionItemId(), itemVo.getItemId())).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(prescriptionItemVos)) {
                    tissueStockAllLog(prescriptionApplyVo, prescriptionItemVos.get(0), drugStockVo, vo.getNum(),
                            DrugConstants.NINE, DrugConstants.DRUG_STORAGE_TYPE.GRANT_DRUG);
                }
            });
        }
        return Boolean.TRUE;
    }

    @Transactional
    public void calculateInventoryDeductions(PrescriptionApplyVo vo, PrescriptionItemVo itemVo, List<DrugStockVo> drugStockVos) {
        // 符合要求的库存总数量
        int totalQuantity = drugStockVos.stream()
                .mapToInt(DrugStockVo::getQuantity)
                .sum();
        if (CollectionUtil.isEmpty(drugStockVos) || totalQuantity < itemVo.getNum()) {
            throw new RuntimeException("药品" + itemVo.getDrugName() + "(" + itemVo.getPackageSpec() + "),库存为"
                    + totalQuantity / itemVo.getPackageNum() + ",库存不足,不能发药，请联系药房入库后再发放。");
        }
        // 获取处方药品开的数量
        int num = StringUtils.equals(itemVo.getPackSize(), BaseConstants.IS_SMALL_PACK) ?
                itemVo.getNum() : itemVo.getNum() * itemVo.getPackageNum();
        for (DrugStockVo drugStockVo : drugStockVos) {
            // 剩余数量
            int surplusNum = drugStockVo.getQuantity() - num;
            DrugStock drugStock = new DrugStock();
            if (surplusNum >= 0) {
                // 扣减充足的库存

                //修改库存
                drugStock.setQuantity(surplusNum);
                baseMapper.update(drugStock, Wrappers.<DrugStock>lambdaUpdate()
                        .eq(DrugStock::getId, drugStockVo.getId())
                );
                // 记录库存流转日志
                DrugStockAllLog drugStockAllLog = tissueStockAllLog(vo, itemVo, drugStockVo, num, DrugConstants.ONE, DrugConstants.DRUG_STORAGE_TYPE.ISSUE);
                // 记录处方明细扣减信息
                grantStockVsPrescriptionService.tissueAndSaveInfo(drugStockVo.getId(), drugStockVo.getIdenticalInfoId(), num
                        , itemVo.getApplyId(), itemVo.getItemId(), drugStockVo.getBatchNo(), drugStockAllLog.getId());
                break;
            } else {
                // 扣减不足的库存
                int insufficientNum = num - drugStockVo.getQuantity();
                //将剩余的库存设置到初始数量
                itemVo.setNum(insufficientNum);
                num = insufficientNum;
                drugStock.setQuantity(0);
                //修改库存
                baseMapper.update(drugStock, Wrappers.<DrugStock>lambdaUpdate()
                        .eq(DrugStock::getId, drugStockVo.getId())
                );
                // 记录库存流转日志
                DrugStockAllLog drugStockAllLog = tissueStockAllLog(vo, itemVo, drugStockVo, drugStockVo.getQuantity(),
                        DrugConstants.ONE, DrugConstants.DRUG_STORAGE_TYPE.ISSUE);
                // 记录处方明细扣减信息
                grantStockVsPrescriptionService.tissueAndSaveInfo(drugStockVo.getId(), drugStockVo.getIdenticalInfoId(), drugStockVo.getQuantity()
                        , itemVo.getApplyId(), itemVo.getItemId(), drugStockVo.getBatchNo(), drugStockAllLog.getId());
            }
        }
    }

    @Transactional
    public DrugStockAllLog tissueStockAllLog(PrescriptionApplyVo vo, PrescriptionItemVo itemVo,
                                             DrugStockVo drugStockVo, Integer amount, String recordType, String sourceType) {

        DrugStockAllLog allLog = new DrugStockAllLog();
        allLog.setId(SnowIdUtils.uniqueString());
        allLog.setClinicNo(vo.getClinicNo());
        allLog.setDocumentNo(vo.getApplyNo());
        allLog.setRecordType(recordType);
        allLog.setSourceType(sourceType);
        allLog.setDrugInfoId(itemVo.getDrugInfoId());
        allLog.setStockId(drugStockVo.getId());
        allLog.setDrugName(itemVo.getDrugName());
        allLog.setDrugCode(itemVo.getDrugCode());
        if (StringUtils.equals(itemVo.getPackSize(), BaseConstants.IS_SMALL_PACK)) {
            allLog.setSpec(itemVo.getMinSpec());
            allLog.setUnits(itemVo.getMinUnits());
        } else {
            allLog.setSpec(itemVo.getPackageSpec());
            allLog.setUnits(itemVo.getPackageUnits());
        }
        allLog.setPackageNum(itemVo.getPackageNum());
        allLog.setPriceId(drugStockVo.getDrugPriceId());
        allLog.setPackSize(itemVo.getPackSize());
        allLog.setAmount(amount);
        allLog.setCurrentAmount(drugStockVo.getQuantity());
        allLog.setPriceQuotiety(BigDecimal.valueOf(1));
        allLog.setItemPrice(itemVo.getPrice());
        allLog.setTradePrice(itemVo.getTradePrice());
        allLog.setCosts(drugStockVo.getRetailPriceMin().multiply(BigDecimal.valueOf(amount)));
        allLog.setApplyDept(vo.getApplyDept());
        allLog.setApplyPerson(vo.getApplyUser());
        allLog.setRecordDept(vo.getImplementDept());
        allLog.setRecordPerson(LoginHelper.getUserId());
        allLog.setUsePerson(vo.getName());
        allLog.setPrescriptionItemId(itemVo.getItemId());
        allLog.initEntity();
        allLog.setOrgId(vo.getOrgId());
        DrugStockAllLogParamVo drugStockAllLogParamVo = new DrugStockAllLogParamVo();
        drugStockAllLogParamVo.setDrugStockAllLog(allLog);
        drugStockAllLogParamVo.setRecordType(DrugConstants.ONE);
        Boolean b = stockAllLogService.caseRecordTypeSave(drugStockAllLogParamVo);
        return allLog;
    }
}
